/*###################################################################
  > File Name: storage/replica/diskmd/disk_slot.c
  > Author: Vurtune
  > Mail: vurtune@foxmail.com
  > Created Time: Wed 17 Jan 2018 06:32:23 PM PST
###################################################################*/
#include "config.h"

#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/poll.h>
#include <sys/epoll.h>
#include <sys/file.h>
#include <sys/mman.h>
#include <linux/fs.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <libgen.h>
#include <ctype.h>
#include <fcntl.h>
#include <libaio.h>
#include <limits.h>
#include <errno.h>
#include <sys/vfs.h>

#define DBG_SUBSYS S_LIBREPLICA

#include "lich_api.h"
#include "disk_slot.h"

#include "disk.h"

/*
 * 256 个磁盘槽位，上层应用会从存储池路径　和　diskid　寻找到磁盘，
 * disk_slot　通过磁盘引用计数保护磁盘不在被引用时删除
 */

typedef struct {
        //protect disk add/rm

        sy_rwlock_t rwlock;
        plock_t plock;
        disk_t *disk;
        void *worker;
        int thread;
        int private;
} disk_slot_t;

static disk_slot_t *__disk_slot__ = NULL;

inline static  void __disk_slot_init_core(void *arg)
{
        int ret, i;
        disk_slot_t *disk_slot, *p;
        core_t *core = core_self();

        (void) arg;

        disk_slot = variable_get(VARIABLE_DISK_SLOT);
        YASSERT(disk_slot == NULL);
        
        DINFO("disk slot init core[%u]\n", core->hash);
        
        ret = ymalloc((void **)&disk_slot, sizeof(disk_slot_t) * DISK_MAX);
        if (unlikely(ret))
                UNIMPLEMENTED(__DUMP__);

        memset(disk_slot, 0x0, sizeof(disk_slot_t) * DISK_MAX);
        
        for (i = 0; i < DISK_MAX; i++) {
                p = &disk_slot[i];
                ret = plock_init(&p->plock, "disk_slot.lock");
                if (unlikely(ret))
                        UNIMPLEMENTED(__DUMP__);

                p->private = 1;
        }

        disk_slot->thread = variable_thread();
        disk_slot->private = 1;
        variable_set(VARIABLE_DISK_SLOT, disk_slot);
}

int disk_slot_init()
{
        int ret, i;
        disk_slot_t *disk_slot, *p;

        ret = ymalloc((void **)&disk_slot, sizeof(disk_slot_t) * DISK_MAX);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        for (i = 0; i < DISK_MAX; i++) {
                p = &disk_slot[i];
                ret = sy_rwlock_init(&p->rwlock, "disk_slot.lock");
                if (unlikely(ret))
                        GOTO(err_free, ret);
        }

        ret = core_init_register(__disk_slot_init_core, NULL, "disk_slot_register");
        if (unlikely(ret))
                GOTO(err_free, ret);

        disk_slot->private = 0;
        __disk_slot__ = disk_slot;
        
        return 0;

err_free:
        yfree((void **)&disk_slot);
err_ret:
        return ret;
}

static int __disk_slot_rdlock(disk_slot_t *slot)
{
        if (likely(slot->private)) {
                return plock_rdlock(&slot->plock);
        } else {
                return sy_rwlock_rdlock(&slot->rwlock);
        }
}       

static int __disk_slot_wrlock(disk_slot_t *slot)
{
        if (likely(slot->private)) {
                return plock_wrlock(&slot->plock);
        } else {
                return sy_rwlock_wrlock(&slot->rwlock);
        }
}       


static void __disk_slot_unlock(disk_slot_t *slot)
{
        if (likely(slot->private)) {
                plock_unlock(&slot->plock);
        } else {
                sy_rwlock_unlock(&slot->rwlock);
        }
}       

int disk_slot_get(int idx, disk_t **disk)
{
        int ret;

        *disk = NULL;
        if (idx > DISK_MAX || !disk || !__disk_slot__)
                return EPERM;

        disk_slot_t *slot = &__disk_slot__[idx];
        ret = __disk_slot_rdlock(slot);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        if (unlikely(!(slot->disk))) {
                __disk_slot_unlock(slot);
                return ENODEV;
        }

        *disk = slot->disk;

        return 0;
err_ret:
        return ret;
}

static int __disk_slot_connect__(const disk_slot_t *slot, disk_slot_t *newslot)
{
        int ret;
        disk_t *newdisk;

        ret = __disk_slot_wrlock(newslot);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        if (newslot->disk) {
                DINFO("disk[%u] already connected\n", slot->disk->idx);
                goto out;
        }

        ret = ymalloc((void **)&newdisk, sizeof(*newdisk));
        if (unlikely(ret))
                UNIMPLEMENTED(__DUMP__);

        memset(newdisk, 0x0, sizeof(*newdisk));
        newdisk->private = 1;
        ret = plock_init(&newdisk->plock, "disk.lock");
        if (unlikely(ret))
                GOTO(err_lock, ret);

        ret = slot->disk->dop->connect(slot->disk, newdisk);
        if (unlikely(ret))
                GOTO(err_free, ret);

        newslot->disk = newdisk;
        
out:
        __disk_slot_unlock(newslot);
        
        return 0;
err_free:
        yfree((void **)&newdisk);
err_lock:
        __disk_slot_unlock(newslot);
err_ret:
        return ret;
}

static int __disk_slot_connect(int idx, disk_slot_t *newslot)
{
        int ret;
        disk_slot_t *slot = &__disk_slot__[idx];

        ret = __disk_slot_rdlock(slot);
        if (unlikely(ret))
                GOTO(err_ret, ret);
        
        if (unlikely(!(slot->disk))) {
                ret = ENODEV;
                GOTO(err_lock, ret);
        }
        
        ret = __disk_slot_connect__(slot, newslot);
        if (unlikely(ret))
                GOTO(err_lock, ret);
        
        __disk_slot_unlock(slot);

        return 0;
err_lock:
        __disk_slot_unlock(slot);
err_ret:
        return ret;
}

#if 1
int disk_slot_private_connect(int idx, disk_t **disk)
{
        int ret;

        *disk = NULL;
        if (idx > DISK_MAX || !disk || !__disk_slot__)
                return EPERM;

        disk_slot_t *pub = &__disk_slot__[idx];
        if (unlikely(!(pub->disk))) {
                ret = ENODEV;
                GOTO(err_ret, ret);
        }        

        if (!disk_avaiable(pub->disk)) {
                ret = EIO;
                GOTO(err_ret, ret);
        }

        disk_slot_t *array = variable_get(VARIABLE_DISK_SLOT);
        disk_slot_t *slot = &array[idx];

        if (unlikely(!(slot->disk))) {
                ret = __disk_slot_connect(idx, slot);
                if (unlikely(ret))
                        GOTO(err_ret, ret);
        }
        
        ret = __disk_slot_rdlock(slot);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        if (unlikely(!(slot->disk))) {
                __disk_slot_unlock(slot);
                ret = ENODEV;
                GOTO(err_ret, ret);
        }

        *disk = slot->disk;

        return 0;
err_ret:
        return ret;
}

int disk_slot_private_disconnect(int idx)
{
        int ret;
        disk_slot_t *array = variable_get(VARIABLE_DISK_SLOT);
        disk_slot_t *slot = &array[idx];

        ret = __disk_slot_wrlock(slot);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        if (unlikely(!(slot->disk))) {
                ret = ENODEV;
                GOTO(err_lock, ret);
        }
        
        slot->disk->dop->disconnect(slot->disk);

        yfree((void **)&slot->disk);
        slot->disk = NULL;

        __disk_slot_unlock(slot);

        return 0;
err_lock:
        __disk_slot_unlock(slot);
err_ret:
        return ret;
}


void disk_slot_private_release(int idx)
{
        if (idx > DISK_MAX || !__disk_slot__) {
                YASSERT(0 && "why ?");
        }

        disk_slot_t *array = variable_get(VARIABLE_DISK_SLOT);
        disk_slot_t *slot = &array[idx];
        __disk_slot_unlock(slot);
}

#else

int disk_slot_private_connect(int idx, disk_t **_disk)
{
        int ret;
        disk_t *disk;

        ret = disk_slot_get(idx, &disk);
        if (unlikely(ret))
                GOTO(err_ret, ret);


        if (!disk_avaiable(disk)) {
                ret = EIO;
                GOTO(err_ret, ret);
        }

        *_disk = disk;

        return 0;
err_ret:
        return ret;
}

void disk_slot_private_release(int idx)
{
        disk_slot_release(idx);
}

int disk_slot_private_disconnect(int idx)
{
        (void) idx;
        return 0;
}

#endif

void disk_slot_release(int idx)
{
        if (idx > DISK_MAX || !__disk_slot__) {
                YASSERT(0 && "why ?");
        }

        disk_slot_t *slot = &__disk_slot__[idx];
        __disk_slot_unlock(slot);
}

int disk_slot_set(int idx, disk_t *disk, void *worker)
{
        int ret;

        //disk 可以是 NULL
        if (idx > DISK_MAX || !__disk_slot__)
                return EPERM;

        disk_slot_t *slot = &__disk_slot__[idx];

        ret = __disk_slot_wrlock(slot);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        slot->disk = disk;

        if (worker)
                slot->worker = worker;

        __disk_slot_unlock(slot);

        return 0;
err_ret:
        return ret;
}

int disk_slot_remove(int idx, disk_t **disk, void **worker)
{
        int ret;

        //disk 可以是 NULL
        if (idx > DISK_MAX || !__disk_slot__)
                return EPERM;

        disk_slot_t *slot = &__disk_slot__[idx];

        ret = __disk_slot_wrlock(slot);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        if (disk)
                *disk = slot->disk;

        if (worker)
                *worker = slot->worker;

        slot->disk = NULL;
        slot->worker = NULL;

        __disk_slot_unlock(slot);

        return 0;
err_ret:
        return ret;
}

int disk_slot_empty(int idx,  int *empty)
{
        int ret;

        *empty = 1;
        if (idx > DISK_MAX || !__disk_slot__)
                return EPERM;

        disk_slot_t *slot = &__disk_slot__[idx];

        ret = __disk_slot_rdlock(slot);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        if (unlikely(slot->disk))
                *empty = 0;

        __disk_slot_unlock(slot);

        return 0;
err_ret:
        return ret;
}

int disk_slot_iterator(func_int1_t func_cb, void *arg)
{
        int ret, i;
        disk_t *disk;

        for (i = 0; i < DISK_MAX; i++) {
                ret = disk_slot_get(i, &disk);
                if (unlikely(ret)) {
                        if (ret != ENODEV)
                                GOTO(err_ret ,ret);
                        continue;
                }

                ret = func_cb(disk, arg);
                if (unlikely(ret))
                        GOTO(err_release, ret);

                disk_slot_release(i);
        }

        return 0;
err_release:
        disk_slot_release(i);
err_ret:
        return ret;
}

int disk_slot_rdlock(disk_t *disk)
{
        int ret;

        if (likely(disk->private)) {
                ret = plock_rdlock(&disk->plock);
        } else {
                ret = sy_rwlock_rdlock(&disk->lock);
        }
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

int disk_slot_wrlock(disk_t *disk)
{
        int ret;

        if (likely(disk->private)) {
                ret = plock_wrlock(&disk->plock);
        } else {
                ret = sy_rwlock_wrlock(&disk->lock);
        }
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

void disk_slot_unlock(disk_t *disk)
{
        if (likely(disk->private)) {
                plock_unlock(&disk->plock);
        } else {
                sy_rwlock_unlock(&disk->lock);
        }
}
